/* 
 * 
 * Proj. Wireless - UFRN
 * JFrameMasterStep
 * 
 */

package gui;

import API_Jung.PopupVertexEdgeMenuMousePlugin;
import API_Jung.MyMouseMenusToDo;
import edu.uci.ics.jung.algorithms.layout.KKLayout;
import edu.uci.ics.jung.graph.DirectedSparseGraph;
import edu.uci.ics.jung.visualization.FourPassImageShaper;
import edu.uci.ics.jung.visualization.LayeredIcon;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.EditingModalGraphMouse;
import edu.uci.ics.jung.visualization.control.ModalGraphMouse;
import edu.uci.ics.jung.visualization.decorators.EllipseVertexShapeTransformer;
import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
import edu.uci.ics.jung.visualization.decorators.VertexIconShapeTransformer;
import edu.uci.ics.jung.visualization.renderers.DefaultEdgeLabelRenderer;
import edu.uci.ics.jung.visualization.renderers.DefaultVertexLabelRenderer;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Shape;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Collection;
import java.util.Iterator;
import java.util.StringTokenizer;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import metodologia.TestNode;
import metodologia.ToDoNode;
import org.apache.commons.collections15.Transformer;
import org.apache.commons.collections15.Factory;

public class JFMStep extends javax.swing.JFrame implements java.io.Serializable {

    private String title; //Titulo do software
    
    private int nodetype, nodecount, nTests, nEdges;
    private DirectedSparseGraph<ToDoNode, String> g;
    private DirectedSparseGraph<TestNode, String> g_pai;
    private ToDoNode start, end;
    private TestNode vertex; //Varivel que armazena o vertice referente a essa tela
    
    //Variáveis da interface
    private KKLayout<ToDoNode, String> layout;
    private VisualizationViewer<ToDoNode, String> vv;
    private JScrollPane graphScroll;
    
    private boolean novo;

    public JFMStep(TestNode vertex) {
        
        //setLocationRelativeTo(null); 
        
        initComponents();
        
        //Configuração da tela
        ImageIcon imagemTituloJanela = new ImageIcon(getClass().getResource("/ImageFiles/icon_PW.png"));  
        setIconImage(imagemTituloJanela.getImage());
        title = "PW-Execute";
        setTitle(title);
        createToolBar();
        this.setMinimumSize(new Dimension(750, 450));
        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        
        this.vertex = vertex;
        g = this.vertex.getG();
        g_pai = new DirectedSparseGraph<TestNode, String>();
        
        layout = new KKLayout(g);
        layout.setSize(new Dimension(300, 300));
        
        nodetype = 0;
        nodecount = 0;
        nTests = 1;
        nEdges = 1;
        
        createGraphScreen();
        
        this.addWindowListener(new WindowAdapter() {

            @Override
            public void windowClosing(WindowEvent we) {
                for (Iterator it = g.getVertices().iterator(); it.hasNext();) {
                    ToDoNode object = (ToDoNode) it.next();
                    object.setX(layout.getX(object));
                    object.setY(layout.getY(object));
                }
            }
        });

        onCloseWindows();
    }

    /*
     * Cria a barra de ferramentas da tela
     */
    public final void createToolBar() {

    }

    /*
     * Constrói a tela inicial sem dados dos vertices
     */
    public final void createGraphScreen() {
        JTabbedPane GraphS = new JTabbedPane();

        vv = new VisualizationViewer<ToDoNode, String>(layout);
        vv.setPreferredSize(new Dimension(300, 300));
        vv.setBackground(Color.WHITE);

        // Show vertex and edge labels

        vv.getRenderContext().setVertexLabelTransformer(new Transformer<ToDoNode, String>() {

            @Override
            public String transform(ToDoNode v) {
                switch (v.getType()) {
                    case 0:
                        return "";
                    case 1:
                        return "";
                    case 2:
                        return ""; //Descrição
                }
                return "Erro";
            }
        });

        vv.getRenderContext().setEdgeLabelTransformer(new Transformer<String, String>() {

            @Override
            public String transform(String e) {
                return "";
            }
        });

        vv.getRenderContext().setVertexIconTransformer(
                new MyVertexIconTransformer<ToDoNode>());
        vv.getRenderContext().setVertexShapeTransformer(
                new MyVertexIconShapeTransformer<TestNode>(new EllipseVertexShapeTransformer<ToDoNode>()));
        vv.getRenderContext().setEdgeLabelRenderer(new DefaultEdgeLabelRenderer(Color.cyan));
        
        vv.setVertexToolTipTransformer(vv.getRenderContext().getVertexLabelTransformer());

        Factory<ToDoNode> vertexFactory = new VertexFactory();
        Factory<String> edgeFactory = new EdgeFactory();

        final EditingModalGraphMouse<ToDoNode, String> graphMouse =
                new EditingModalGraphMouse<ToDoNode, String>(vv.getRenderContext(), vertexFactory, edgeFactory);

        PopupVertexEdgeMenuMousePlugin myPlugin = new PopupVertexEdgeMenuMousePlugin();
        // Add some popup menus for the edges and vertices to our mouse plugin.
        JPopupMenu vertexMenu = new MyMouseMenusToDo.VertexMenu2(g, vv);
        JPopupMenu EdgeMenu = new MyMouseMenusToDo.EdgeMenu2();

        myPlugin.setVertexPopup(vertexMenu);
        myPlugin.setEdgePopup(EdgeMenu);
        graphMouse.remove(graphMouse.getPopupEditingPlugin());  // Removes the existing popup editing plugin

        graphMouse.add(myPlugin);   // Add our new plugin to the mouse

        vv.setGraphMouse(graphMouse);
        vv.addKeyListener(graphMouse.getModeKeyListener());

        graphMouse.setMode(ModalGraphMouse.Mode.TRANSFORMING);

        graphScroll = new JScrollPane(vv);
        graphScroll.setSize(this.GraphScreen.getSize());

        GraphS.addTab("Etapas", graphScroll);

        GraphScreen.setLayout(new BorderLayout());
        GraphScreen.add("Center", GraphS);
        Collection<ToDoNode> col = g.getVertices();
        if (!col.isEmpty()) {
            reloadGraphScreen();
        } else {
            vv.repaint();
        }
        
    }

    /*
     * Reconstrói a tela com os dados de "dadosMetodologia"
     */
    public final void reloadGraphScreen() {

        layout.setGraph(g);
        for (Iterator it = g.getVertices().iterator(); it.hasNext();) {
            ToDoNode object = (ToDoNode) it.next();
            layout.setLocation(object, new Point2D.Double(object.getX(), object.getY()));         
            if (object.getType() == 2) {
                nTests++;
            }
            if(object.getType() == 0){ start = object; }
            else if (object.getType() == 1) { end = object; }
            
        }

        vv.getRenderContext().setVertexLabelTransformer(new Transformer<ToDoNode, String>() {

            @Override
            public String transform(ToDoNode v) {
                switch (v.getType()) {
                    case 0:
                        return "Inicio";
                    case 1:
                        return "Final";
                    case 2:
                        return "List" + " - " + v.getCatid(); //Descrição
                }
                return "Erro";
            }
        });
        
        vv.setGraphLayout(layout);
        Factory<ToDoNode> vertexFactory = new VertexFactory();
        Factory<String> edgeFactory = new EdgeFactory();
        final EditingModalGraphMouse<ToDoNode, String> graphMouse =
                new EditingModalGraphMouse<ToDoNode, String>(vv.getRenderContext(), vertexFactory, edgeFactory);

        PopupVertexEdgeMenuMousePlugin myPlugin = new PopupVertexEdgeMenuMousePlugin();
        // Add some popup menus for the edges and vertices to our mouse plugin.
        JPopupMenu vertexMenu = new MyMouseMenusToDo.VertexMenu2(g, vv);

        JPopupMenu EdgeMenu = new MyMouseMenusToDo.EdgeMenu2();

        myPlugin.setVertexPopup(vertexMenu);
        myPlugin.setEdgePopup(EdgeMenu);
        graphMouse.remove(graphMouse.getPopupEditingPlugin());  // Removes the existing popup editing plugin

        graphMouse.add(myPlugin);   // Add our new plugin to the mouse

        vv.setGraphMouse(graphMouse);
        vv.addKeyListener(graphMouse.getModeKeyListener());

        graphMouse.setMode(ModalGraphMouse.Mode.TRANSFORMING);
        
        nEdges = g.getEdgeCount();
        nodecount = g.getVertexCount();

        this.graphScroll.setViewportView(vv);
        
        //Colocando o titulo
        setTitle(vertex.getNome() + " - " + title);
    }

    public DirectedSparseGraph<ToDoNode, String> getG(){
        return g;
    }
    
    public void setG(DirectedSparseGraph<ToDoNode, String> g) {
        this.g = g;
    }

    public DirectedSparseGraph<TestNode, String> getG_pai() {
        return g_pai;
    }

    public void setG_pai(DirectedSparseGraph<TestNode, String> g_pai) {
        this.g_pai = g_pai;
    }
    
    public int getNTests() {
        return nTests;
    }

    /**
     * @return the novo
     */
    public boolean isNovo() {
        return novo;
    }

    /**
     * @param novo the novo to set
     */
    public void setNovo(boolean novo) {
        this.novo = novo;
    }

    public class MyVertexIconTransformer<ToDoNode> implements
            Transformer<ToDoNode, Icon> {
        
         /*
         * 
         * Construir função que verifica todos os nós posteriores a <TestNode>.
         *
         * OBS: Não precisei fazer esta função, apenas adicionei outra condição no if, da função defineState, que diz que um no deve ficar verde 
         * e retirei todas as condiçoes para um no ficar vermelho, ou seja, para um no ficar vermelho basta nao entrar no caso dele ser verde e tambem nao entrar no caso dele ser azul
         *
         * OBS2: Para resolver o problema de mudança de cor automaticamente apenas dei um vv.repaint()antes de retonar um icone!
         * 
         */
      
        @Override
        public Icon transform(ToDoNode n) {
            String[] n2 = n.toString().split("nici");
            String[] n3 = n.toString().split("ina");
            Icon icone;

            if (n2.length > 1) {
                icone = new LayeredIcon(new ImageIcon(getClass().getResource("/ImageFiles/StartBall.png")).getImage());
                start.setState(3);
                return icone;
            } else if (n3.length > 1) {
                icone = new LayeredIcon(new ImageIcon(getClass().getResource("/ImageFiles/EndNode.png")).getImage());
                return icone;
            } else {  
                
                //Guarda o valor do catId do toDonode em execução
                String aux = "";
                StringTokenizer st = new StringTokenizer(n.toString(), "- ");  
                while (st.hasMoreTokens()){
                    aux = st.nextToken();
                }
                int catId = Integer.parseInt(aux);
                
                // Função que implementa o lógica de mudança de cores.
                defineState(g,catId);
                //atualiza automaticamente a cor de todos os nos.
                vv.repaint();                
                // Invoca a função "readIcon" que define o icone a partir do estado do nó.
                icone = new LayeredIcon(new ImageIcon(getClass().getResource(readIcon(g, catId))).getImage());
                return icone;        
            }
        }
        
    }

    public static class MyVertexIconShapeTransformer<TestNode> extends VertexIconShapeTransformer<ToDoNode> {

        boolean shapeImages = true;

        public MyVertexIconShapeTransformer(Transformer<ToDoNode, Shape> delegate) {
            super(delegate);
        }

        /**
         * @return Returns the shapeImages.
         */
        public boolean isShapeImages() {
            return shapeImages;
        }

        /**
         * @param shapeImages The shapeImages to set.
         */
        public void setShapeImages(boolean shapeImages) {
            shapeMap.clear();
            this.shapeImages = shapeImages;
        }

        public Icon getIcon(ToDoNode n) {
            String[] n2 = n.toString().split("nici");
            String[] n3 = n.toString().split("ina");
            if (n2.length > 1) {
                Icon icon = new LayeredIcon(new ImageIcon(getClass().getResource("/ImageFiles/StartBall.png")).getImage());
                return icon;
            } else if (n3.length > 1) {
                Icon icon = new LayeredIcon(new ImageIcon(getClass().getResource("/ImageFiles/EndNode.png")).getImage());
                return icon;
            } else {
                Icon icon = new LayeredIcon(new ImageIcon(getClass().getResource("/ImageFiles/TestNode.png")).getImage());
                return icon;
            }
        }

        @Override
        public Shape transform(ToDoNode v) {
            Icon icon = getIcon(v);

            if (icon != null && icon instanceof ImageIcon) {

                Image image = ((ImageIcon) icon).getImage();

                Shape shape = shapeMap.get(image);
                if (shape == null) {
                    if (shapeImages) {
                        shape = FourPassImageShaper.getShape(image, 30);
                    } else {
                        shape = new Rectangle2D.Float(0, 0,
                                image.getWidth(null), image.getHeight(null));
                    }
                    if (shape.getBounds().getWidth() > 0
                            && shape.getBounds().getHeight() > 0) {
                        int width = image.getWidth(null);
                        int height = image.getHeight(null);
                        AffineTransform transform =
                                AffineTransform.getTranslateInstance(-width / 2, -height / 2);
                        shape = transform.createTransformedShape(shape);
                        shapeMap.put(image, shape);
                    }
                }
                return shape;
            } else {
                return delegate.transform(v);
            }
        }
    }

    class VertexFactory implements Factory<ToDoNode> {

        @Override
        public ToDoNode create() {
            switch (nodetype) {
                case 0:
                    System.out.println("nodetype = " + nodetype);
                    start = new ToDoNode(nodecount++, nodetype, 0);
                    return start;
                case 1:
                    System.out.println("nodetype = " + nodetype);
                    end = new ToDoNode(nodecount++, nodetype, 0);
                    return new ToDoNode(nodecount++, nodetype, 0);
                case 2:
                    System.out.println("nodetype = " + nodetype);
                    return new ToDoNode(nodecount++, nodetype, nTests++);
            }
            return new ToDoNode(nodecount++, nodetype, 0);
        }
    }

    class EdgeFactory implements Factory<String> {

        @Override
        public String create() {
            return "" + nEdges++;
        }
    }


    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        buttonGroup1 = new javax.swing.ButtonGroup();
        jToolBar1 = new javax.swing.JToolBar();
        GraphScreen = new javax.swing.JPanel();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        jToolBar1.setFloatable(false);
        getContentPane().add(jToolBar1, java.awt.BorderLayout.NORTH);

        GraphScreen.setLayout(new java.awt.BorderLayout());
        getContentPane().add(GraphScreen, java.awt.BorderLayout.CENTER);

        pack();
    }// </editor-fold>//GEN-END:initComponents
    
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JPanel GraphScreen;
    private javax.swing.ButtonGroup buttonGroup1;
    private javax.swing.JToolBar jToolBar1;
    // End of variables declaration//GEN-END:variables

    /*
     * define(true) se todos os nos(ToDoNode) da JFMStep(todoscreen) de um TestNode estao todos verdes(state = 3)
     */
    public boolean allFinished() { 
        boolean aux = true;
        for (Iterator it = g.getEdges().iterator(); it.hasNext();) { // percorre todas as arestas do grafo
            String edge = (String) it.next();
        
            if (!g.getEndpoints(edge).getFirst().toString().equals(start.toString()) && aux == true) { // se a aresta vier do start e se aux for falso
                if (g.getEndpoints(edge).getFirst().getState() == 3 || g.getEndpoints(edge).getFirst().getToDo().isFinished()) { // se vier de uma vertice verde(state = 3)
                    aux = true;
                } else {
                    aux = false;
                }
            }
        }
        return aux;
    }
    
    /*
     * Método que, através do estado, define o icone do nó.
     */
    private String readIcon(DirectedSparseGraph<ToDoNode, String> g, int catId){
        String aux1 = ("/ImageFiles/" + "Logo2.png");

        for (Iterator it = g.getEdges().iterator(); it.hasNext();) { // percorre todas as arestas
            String edge = (String) it.next();
            
            if ((g.getEndpoints(edge).getSecond().getCatid() == catId)&&(g.getEndpoints(edge).getSecond().getType() == 2)) { // seleciona as arestas q chegao no Catid                
                if (g.getEndpoints(edge).getSecond().getState() == 1) { // se state = 1
                    aux1 = ("/ImageFiles/" + "targetBall.png");// no azul
                } else if (g.getEndpoints(edge).getSecond().getState() == 2) { // se state = 2
                    aux1 = ("/ImageFiles/" + "TestNode.png"); // no vermelho
                } else if (g.getEndpoints(edge).getSecond().getState() == 3) { // se state = 3
                    aux1 = ("/ImageFiles/" + "TrueBall.png"); // no verde
                } else if (g.getEndpoints(edge).getSecond().getState() == 0){
                    aux1 = ("/ImageFiles/" + "Save.png"); //Só para teste
                } else {
                    aux1 = ("/ImageFiles/" + "Open.png"); //Só para teste
                }
            } 
        }
        return (aux1);
    }
    
    /*
     * Método que verifica se os adjacentes que chegam estão concluídos.
     */
    private boolean neighborsFinished(DirectedSparseGraph<ToDoNode, String> g, int catId){
        
        int aux = 0; //Variável diferente de zero representa que tem nó com estado não terminado.

        //Realiza a busca do nó pelo seu catId e armazena na variável 'node'.
        for (Iterator it = g.getEdges().iterator(); it.hasNext();) {
            String edge = (String) it.next();

            if (g.getEndpoints(edge).getSecond().getCatid() == catId){
                if ((g.getEndpoints(edge).getFirst().getState() != 3) && (g.getEndpoints(edge).getFirst().getType() == 2)){
                    aux++;
                }
            }
        }

        //Teste final
        if (aux==0){
            return true;
        } else {
            return false;
        }
        
    }
    

    /*
     * Método que define o estado do nó.
     */
    private void defineState(DirectedSparseGraph<ToDoNode, String> g, int catId){

        for (Iterator it = g.getEdges().iterator(); it.hasNext();) { // passa por todas as arestas
            String edge = (String) it.next();

            if (g.getEndpoints(edge).getSecond().getCatid() == catId) { // seleciona as arestas q chegao ao catId

                if (g.getEndpoints(edge).getSecond().getToDo().isFinished() && neighborsFinished(g,catId)) {
                    g.getEndpoints(edge).getSecond().setState(3);
                } else if (neighborsFinished(g,catId)) { // se os vertices que chegao no catId forem verdes ou se vinher do Start
                    g.getEndpoints(edge).getSecond().setState(1); // seta o state como 1(azul)
                } else  { // se os vertices que chegao no catId nao forem verdes ou se nao vinher do Start
                    g.getEndpoints(edge).getSecond().setState(2); // seta o state como 2(Vermelho)
                    
                    g.getEndpoints(edge).getSecond().getToDo().dispose();   
                }
            }            
        }
    }
    
    /*
     * funcao que ira fechar todas as ToDo de cada no(desta tela), 
     * quando o no referente a esta tela, na tela anterior, ficar vermelho
     */
    public void disposeAll() {
        for (Iterator it = g.getEdges().iterator(); it.hasNext();) { // percorre todas as arestas do grafo
            String edge = (String) it.next();

            g.getEndpoints(edge).getSecond().getToDo().dispose();
        }
    }
    
    /*
     * Método que carrega o progresso salvo no arquivo
     */
    public void loadProgress(int List, boolean[] checkBox) {
        
        for (Iterator it = g.getEdges().iterator(); it.hasNext();) { // percorre todas as arestas
            String edge = (String) it.next();

            if(g.getEndpoints(edge).getSecond().getId() == List) {
                g.getEndpoints(edge).getSecond().getToDo().setCheckBox(checkBox);
            }
        }
        
    }
    
        /*
     * Evento para quando fechar a tela
     */
    private void onCloseWindows(){
        addWindowListener(new WindowAdapter() {  
            @Override
            public void windowClosed(WindowEvent e) { 
                disposeAll();
            }  
        });
    }
    
    public String[] relatorioSimples(String[] conteudo, int Catid2) {
        for (Iterator it = g.getEdges().iterator(); it.hasNext();) { // percorre todas as arestas
            String edge = (String) it.next();

            if(g.getEndpoints(edge).getSecond().getCatid() == Catid2){
                conteudo[1] = Integer.toString(g.getEndpoints(edge).getSecond().getCatid());
                conteudo[2] = g.getEndpoints(edge).getSecond().getToDo().getPorcentagem();
                conteudo[3] = Integer.toString(g.getEndpoints(edge).getSecond().getToDo().getProgress());
                conteudo[4] = Integer.toString(g.getEndpoints(edge).getSecond().getToDo().getQtyBox());
            }
        }
        return conteudo;
    }
        
    public String[] relatorioSimples2(String[] conteudo, int Catid2) {
        for (Iterator it = g.getEdges().iterator(); it.hasNext();) { // percorre todas as arestas
            String edge = (String) it.next();

            if(g.getEndpoints(edge).getSecond().getCatid() == Catid2){
                conteudo[1] = Integer.toString(g.getEndpoints(edge).getSecond().getCatid());
                conteudo[2] = g.getEndpoints(edge).getSecond().getToDo().getCheckBoxMarcado();
                conteudo[5] = "dsgf";
            }
        }
        return conteudo;
    }
}
